feat: add send-event and send-envelope commands with DSN auth#921
Conversation
Implements sentry send-event and sentry send-envelope — the first
commands that authenticate via a DSN (not a Bearer token), matching
the old sentry-cli behaviour.
Architecture:
- src/lib/envelope/transport.ts: shared DSN-based envelope sender
using @sentry/core (makeDsn, getEnvelopeEndpointWithUrlEncodedAuth,
serializeEnvelope) — no new dependencies
- src/lib/envelope/event-builder.ts: builds a Sentry Event from CLI
flags (message, level, tags, extras, user, fingerprint, etc.)
- auth: 'dsn' in buildCommand: skips Bearer token guard and RC URL
check for DSN-only commands
send-event flags (matching old CLI):
--dsn, -m/--message, -l/--level, -r/--release, -E/--env,
-t/--tag (variadic), -e/--extra (variadic), -u/--user (variadic),
-f/--fingerprint (variadic), --dist, --platform, --timestamp,
--no-environ, --raw
send-envelope flags:
--dsn, --raw (send bytes without parsing)
Tests: 48 new tests across 4 files (TDD — tests written first)
- transport: URL construction, auth params, error handling
- event-builder: parseKeyValue, parseUserFields, buildEventFromFlags
- send-event command: inline, file, JSON output, missing DSN
- send-envelope command: valid file, raw mode, invalid envelope,
multiple files, missing DSN
|
Codecov Results 📊✅ 6907 passed | Total: 6907 | Pass Rate: 100% | Execution Time: 0ms 📊 Comparison with Base Branch
All tests are passing successfully. ✅ Patch coverage is 82.00%. Project has 14399 uncovered lines. Files with missing lines (4)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
- Coverage 77.04% 76.68% -0.36%
==========================================
Files 319 324 +5
Lines 60950 61734 +784
Branches 0 0 —
==========================================
+ Hits 46956 47335 +379
- Misses 13994 14399 +405
- Partials 0 0 —Generated by Codecov Action |
sentry send event -- canonical new interface sentry send envelope sentry send-event -- hidden bw-compat alias (old sentry-cli) sentry send-envelope -- hidden bw-compat alias (old sentry-cli)
- parseTimestamp now throws ValidationError on invalid input instead of silently falling back to Date.now() (Seer/Cursor) - Environ spread order fixed: process.env goes first so user --extra environ:val correctly overrides it (Cursor) - Raw mode no longer reads file twice: decode bytes in-memory instead of re-reading (Cursor) - Fixed --raw help text: sends raw bytes directly, not 'inside an envelope' (Sentry) - send-envelope now errors when no files provided (Sentry) - send-event --raw in inline mode now throws ValidationError (Sentry)
|
Fixed all 6 bot findings in 7e33546:
|
- Remove undocumented DSN auto-detection claim from send-event.ts module comment and requireDsn error message (only --dsn and SENTRY_DSN are actually supported) - Use Number.isFinite instead of !Number.isNaN to reject Infinity and -Infinity as timestamp values
- parseTimestamp: remove && num > 0 guard — epoch-0 and negative timestamps are valid; the old guard silently corrupted them to year 2000 / year 3600 via Date.parse fallthrough - resolveDsn: trim whitespace from --dsn flag and SENTRY_DSN env var (leading space / trailing newline from shell caused confusing error) - transport test: use .toThrow(ValidationError) not bare .toThrow() - send.md: remove false claim that DSN auto-detects from project files - buildFilePayload + send-envelope: wrap Bun.file reads in try/catch, surface ENOENT and parse errors as ValidationError not raw stacks - Document that --message is ignored when file args are provided - Add tests: ENOENT → ValidationError, --raw without file → error, no files for send-envelope → ValidationError, DSN whitespace trim, whitespace trim for SENTRY_DSN env var
…eation in try/catch - Centralise ENOENT/IO error handling into shared readFileBytes() in transport.ts - Removes duplicated file-reading error block from both send-event.ts and send-envelope.ts - Wrap makeDsn() call in try/catch to guard against future SDK internal throws - Wrap createEventEnvelope()+serializeEnvelope() in try/catch with descriptive ValidationError Addresses Sentry Seer findings (medium) and Cursor Bugbot finding (low) on PR #921.
…atch Mirrors the file-based path's error handling — catches any internal SDK errors and re-throws as ValidationError.
Calling buildEnvelopeUrl(dsn) before the file read loop ensures invalid DSNs are caught upfront rather than after unnecessary I/O, consistent with send-event's behavior.
getEnvelopeEndpointWithUrlEncodedAuth appends /<version> internally, so passing 'sentry-cli/dev' produced the malformed identifier sentry_client=sentry-cli/dev/dev on every envelope request. Now passes bare name 'sentry-cli' and lets the SDK append '/dev'.
…r message - makeDsn may throw SentryError internally; wrap in try/catch for safety - requireDsn error message now uses canonical 'sentry send event' instead of hardcoded 'sentry send-event' (wrong for send-envelope callers)
- Canonical command is now 'sentry event send' under the event route - send-envelope is now a deprecation shim suggesting 'sentry event send --raw' - send-event remains as a hidden backward-compat alias - Removed the 'send' route group entirely - Updated docs, tests, and all references
Prevents HostScopeError from masking the deprecation message when a .sentryclirc file is present.
# Conflicts: # AGENTS.md
# Conflicts: # docs/src/content/docs/contributing.md # plugins/sentry-cli/skills/sentry-cli/references/event.md
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b6ec9ac. Configure here.
| const extra: Record<string, unknown> = { | ||
| ...(flags["no-environ"] ? {} : { environ: process.env }), | ||
| ...parseKeyValuePairs(flags.extra), | ||
| }; |
There was a problem hiding this comment.
Default environ leaks auth tokens in event extras
Medium Severity
When --no-environ is not set (the default), process.env is included verbatim as extra.environ in the event payload. In CI environments where SENTRY_AUTH_TOKEN is set alongside SENTRY_DSN, running sentry event send -m "deploy done" silently sends the auth token (and any other secrets like DATABASE_URL, AWS_SECRET_ACCESS_KEY, etc.) to the Sentry ingest endpoint, where they become visible to all project members. The --no-environ default of false makes this a passive credential leak.
Reviewed by Cursor Bugbot for commit b6ec9ac. Configure here.


Summary
Implements
sentry event sendfor old sentry-cli parity. Ref #600.This is the first command in the new CLI that authenticates via a DSN (not a Bearer token), so no
sentry auth loginis needed — provide a DSN from--dsn,SENTRY_DSNenv var, or auto-detection.Command structure
sentry event sendSend a Sentry event from CLI flags or a JSON file:
Supports all flags from old
sentry-cli send-event:--message,--level,--release,--env,--tag,--extra,--user,--fingerprint,--timestamp,--dist,--platform,--no-environ.Design decisions
event sendunder the event group — follows the mutation pattern (project create,issue resolve,release create). Discoverable viasentry event --help.send-envelopedeprecated — envelopes are a low-level concept; the--rawflag onevent sendcovers the use case. Runningsentry send-envelopenow throws with a clear message suggestingsentry event send --raw <file>.auth: "dsn"on buildCommand skips the token guard entirely. DSN is resolved from--dsnflag orSENTRY_DSNenv var.Shared infrastructure
src/lib/envelope/transport.ts—requireDsn(),buildEnvelopeUrl(),sendEnvelopeRequest(),readFileBytes()src/lib/envelope/event-builder.ts—buildEventFromFlags()constructs a Sentry Event from CLI flags@sentry/corefor envelope creation/serialization and DSN parsing